# encoding:utf-8
#qpy:console
import time
import datetime
import requests
requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS = "TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-256-GCM-SHA384:ECDHE:!COMPLEMENTOFDEFAULT"
import json
import re
import copy
import base64
import urllib.parse
from bs4 import BeautifulSoup
from Crypto.Cipher import AES
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5 #用于加密
from Crypto import Random
from Crypto import Hash

class Gitee(): 
    gitee = {
    	    #用户名
    	    "username" : "",
    	    #密码
    	    "password" : "",
    	    #本账户加解密密码
    	    "key" : "",
    	    #加解密关键字
    	    "keyword" : "",
    	    #仓库
    	    "cangku" : "",
    	    #本文件存放位置
    	    "giteeFile" : "",
    	    #用户名密码登录密码加密公钥
    	    "pubkey" : ""
    	}
    def __init__(self,gitee):
        for key,values in gitee.items():
            setattr(self,key,values)
        self.gitee.update(gitee)
        self.session = requests.session()
        self.giteeUrl = {
    	    #登录url
    	    "login":{
    		   	    "title" : "登录url",
    		   	    "url": "https://gitee.com/login",
    		   	    "data" :{
    		   	    	    "encrypt_key" : "password",
    		   	    	    "utf8" : "✓",
                 "authenticity_token" : "使用时设置",
                 "redirect_to_url" : "",
                 "user[login]" :self.username,
                 "encrypt_data[user[password]]" : self.rsaEncrypt(self.password),
                 "user[remember_me]" : "0",
                 "user[remember_me]" : "1"
                 },
             "params" :{},
    		   	    "type": "post"
    		   	    },
    		   #编辑验证url	    
    		   "unread" :{
    		   	    "title" : "编辑验证url",
    		   	    "url": "https://gitee.com/notifications/unread_count",
    		   	    "params" :{},
    		   	    "type": "get"                 
    		   	    },
    		   	#获取文件url	    
    		   "getFile" :{
    		   	    "title" : "获取文件url",
    		   	    "url": "https://gitee.com/" + self.cangku + "/raw/master/filename",
    		   	    "params" :{},
    		   	    "type": "get"                 
    		   	    },
    		   #获取文件url	    
    		   "getCommits" :{
    		   	    "title" : "获取提交列表url",
    		   	    "url": "https://gitee.com/" + self.cangku + "/commits/master/filename",
    		   	    "params" :{},
    		   	    "type": "get"                 
    		   	    },
    		   #新建文件url
    		   "add" :{
    		   	    "title" : "新建文件url",
    		   	    "url": "https://gitee.com/" + self.cangku + "/new/master/",
    		   	    "data" :{
    		   	    	    "utf8" : "✓",
                 "authenticity_token" : "使用时设置",
                 "license" : "no",
                 "file_name" : "使用时设置",
                 "commit_message_header" : "使用时设置",
                 "extended_information" : "",
                 "branch_name" : "master",
                 "file_path" : "使用时设置",
                 "content" : "使用时设置",
                 "button" : ""
                 },
    		   	    "type": "post"                 
    		   	    },
    		   #新建文件夹url
    		   "createFolder" :{
    		   	    "title" : "新建文件夹url",
    		   	    "url": "https://gitee.com/" + self.cangku + "/new/master/",
    		   	    "data" :{
    		   	    	    "utf8" : "✓",
                 "authenticity_token" : "使用时设置",
                 "new_file_path" : "使用时设置",
                 "content" : "",
                 "new_folder_path" : "使用时设置",
                 "button" : "",
                 },
    		   	    "type": "post"                 
    		   	    },
    		   #删除文件夹url
    		   "deleteFolder" :{
    		   	    "title" : "删除文件夹url",
    		   	    "url": "https://gitee.com/" + self.cangku + "/delete/tree/master/foldername" ,
    		   	    "data" :{},
    		   	    "type": "delete"                 
    		   	    },
    		   #更新文件url
    		   "update" :{
    		   	    "title" : "更新文件url",
    		   	    #后面加上filename
    		   	    "url": "https://gitee.com/" + self.cangku + "/edit/master/filename",
    		   	    "data" :{
    		   	    	    "utf8" : "✓",
                 "authenticity_token" : "使用时设置",
                 "_method" : "put",
                 "license" : "no",
                 "file_name" : "使用时设置",
                 "commit_message_header" : "使用时设置",
                 "extended_information" : "",
                 "branch_name" : "master",
                 "file_path" : "使用时设置",
                 "content" : "使用时设置",
                 "button" : "",
                 "eol_crl" : "false",
                 "last_commit_sha" : "使用时设置"
                 },
             "params" :{},
    		   	    "type": "post"                 
    		   	    },
    		   	#删除文件
    		   	"delete" :{
    		   	    "title" : "删除文件url",
    		   	    #后面加上filename
    		   	    "url": "https://gitee.com/" + self.cangku + "/delete/blob/master/filename",
    		   	    "data" :{
    		   	    	    "_method" : "delete",
                 "authenticity_token" : "使用时设置"
                 },
    		   	    "type": "post"                 
    		   	    },
    		    "headers" : {
              "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/79.0.3945.147 Safari/534.24 XiaoMi/MiuiBrowser/13.9.20"
              }
        	}        
        self.login()
    #'''
    #采用AES对称加密算法
    #'''
    # str不是16的倍数那就补足为16的倍数
    @staticmethod 
    def add_to_16(value):
        while len(value) % 16 != 0:
            value += '\0'
        return str.encode(value)  # 返回bytes
    #加密方法
    @staticmethod
    def aesEncrypt(text,key=''):
        # 初始化加密器
        aes = AES.new(Gitee.add_to_16(key), AES.MODE_ECB)
        text = base64.b64encode(text.encode('utf-8')).decode('ascii')
        #先进行aes加密
        encrypt_aes = aes.encrypt(Gitee.add_to_16(text))
        #用base64转成字符串形式
        encrypted_text = str(base64.encodebytes(encrypt_aes), encoding='utf-8')
        return encrypted_text
        
    #解密方法
    @staticmethod
    def aesDecrypt(text,key=''):
        # 初始化加密器
        aes = AES.new(Gitee.add_to_16(key), AES.MODE_ECB)
        #优先逆向解密base64成bytes
        base64_decrypted = base64.decodebytes(text.encode(encoding='utf-8'))
        #执行解密密并转码返回str
        decrypted_text = str(aes.decrypt(base64_decrypted),encoding='utf-8').replace('\0','') 
        decrypted_text = base64.b64decode(decrypted_text.encode('utf-8')).decode('utf-8') 
        return decrypted_text
    
    @staticmethod
    def getGitee(url,key): 
        text = Gitee.get(url) 
        text = Gitee.dataManager(text,Gitee.jieMi,key)
        data = json.loads(text)
        return Gitee(data)
        
    @staticmethod
    def get(url):
        
        html = requests.get(url)       
        if html.status_code == 404:
            return False
        else:
            return html.text
    #传过来的数据根据关键词加解密
    @staticmethod
    def dataManager(data,func,*args) :
        try:
            data = json.loads(data)  
            Gitee.traversalDict(data,func,*args)
            return json.dumps(data,indent=4,ensure_ascii=False)
        except Exception as e:
            return data
            
    #数据加密 
    @staticmethod           
    def jiaMi(text,*args):
        (key,keyword,temp_key) = args
         
        state = False
        for index in range(len(keyword)):
            if keyword[index] in temp_key.lower() :
                state = True
        if state == True:
            return Gitee.aesEncrypt(text,key)
        else:
            return text                           
    #数据解密 
    @staticmethod           
    def jieMi(text,*args):
        key = args[0]
        try:
            text1 = Gitee.aesDecrypt(text,key)
            if text1 != "":
                text = text1
            return text
        except Exception as e:
            return text
                    
            
    #传入字典加解密  
    @staticmethod     
    def traversalDict(dict_a,func,*args):
        #使用isinstance检测数据类型 
        if isinstance(dict_a,dict) :
            for x in range(len(dict_a)):
                temp_key = list(dict_a.keys())[x]
                temp_value = dict_a[temp_key]
                if isinstance(temp_value,str) or isinstance(temp_value,float) or isinstance(temp_value,int):
                    dict_a[temp_key] = func(temp_value,*args,temp_key)
                elif isinstance(temp_value,dict):                      
                    #自我调用实现无限遍历 
                    Gitee.traversalDict(temp_value,func,*args)
                elif isinstance(dict_a,list) :
                    for x in range(len(dict_a)):
                        Gitee.traversalDict(dict_a[x],func,*args)
        
        elif isinstance(dict_a,list) :
            for x in range(len(dict_a)):
                Gitee.traversalDict(dict_a[x],func,*args)
          
            
    #gitee网络传输用户名密码加密      
    def rsaEncrypt(self,plain_text):
        #先公钥加密
        cipher_pub_obj = PKCS1_v1_5.new(RSA.importKey(self.pubkey))
        _secret_byte_obj = cipher_pub_obj.encrypt(plain_text.encode('utf-8'))
        encrypted_text = str(base64.b64encode(_secret_byte_obj),encoding='utf-8')
        return encrypted_text    
               	
    def myRequests(self,url,allowredirects=True):
        
        try:
            if url["type"] == "post":
                html = self.session.post(url=url["url"],data=url["data"],allow_redirects=allowredirects)
            elif url["type"] == "get":
                html = self.session.get(url=url["url"],params=url["params"],allow_redirects=allowredirects)
            elif url["type"] == "delete":
                html = self.session.delete(url=url["url"],data=url["data"],allow_redirects=allowredirects)
            
            return html
        except Exception as e:
            text = url["title"] + "失效" + str(e) 
            print(text)

    	#账户登录
    def login(self):    
        url = self.giteeUrl["login"]
        self.session.headers = self.giteeUrl["headers"]
        html = self.session.get(url="https://gitee.com/login",headers=self.giteeUrl["headers"],cookies = self.cookie)
        if html.text.find("我的工作台") >= 0:
            print("gitee token登录")
            soup = BeautifulSoup(html.text,"html.parser")
            authenticity_token = soup.find(attrs={"name":"csrf-token"})['content']
            self.authenticity_token = authenticity_token
            return True
        #获得authenticity_token
        print(html.text)
        soup = BeautifulSoup(html.text,"html.parser")
        authenticity_token = soup.find(attrs={"name":"csrf-token"})['content']
        self.authenticity_token = authenticity_token
        url["data"]["authenticity_token"] = self.authenticity_token
        html = self.myRequests(url)
        #获得登录后authenticity_token
        soup = BeautifulSoup(html.text,"html.parser")
        self.authenticity_token = soup.find(attrs={"name":"csrf-token"})['content']
         
        if  str(self.session.cookies).find("remember_user_token") >= 0:
            print("gitee 密码登录")
            self.cookie = requests.utils.dict_from_cookiejar(self.session.cookies)
            self.gitee["cookie"] = self.cookie
            self.update("gitee.json",json.dumps(self.gitee,indent=4,ensure_ascii=False))           
            return True
                 
    def getFile(self,filename,commit = 0,key = "",encrypt = True):
        if key == "":
            key = self.key
        filename = self.findFilePath(filename,self.filesys)
        url = self.giteeUrl["getFile"].copy()        
        url["url"] =  url["url"].replace("filename",filename)
        if commit > 0:
            page = int(commit/20.001) +1
            shaList = self.getCommits(filename,page)
            if shaList == []:
                return "版本不存在，参数太大"
            index = commit%20 - 1
            if index ==-1:
                index = 20
            if index > len(shaList) -1 and len(shaList) != 0:
                index =  len(shaList) -1  
            commitSha = shaList[index]["commitSha"]
            url["url"] = url["url"].replace("master",commitSha)
        html = self.session.get(url["url"])
        if html.status_code == 404:
            return False
        else:
            text = html.text
            #含有违规信息则显示这个
            if text == "The content may contain violation information":
                html = self.session.get(url["url"].replace("raw","blob"))        
                text = self.getFileOther(html.text)
            if encrypt == True:
                return Gitee.dataManager(text,Gitee.jieMi,key)
            else:
                return  text
    #如果含有违规信息取不到数据则用这个函数
    def getFileOther(self,text):
        begin = "{&#x000A"
        end = "</textarea>"
        text = text[text.find(begin):text.find(end)]
        text = text.replace('&#x000A;','\n').replace('&quot;','"')
        return text
            
    def getCommits(self,filename,page):
        url = self.giteeUrl["getCommits"].copy()        
        url["url"] =  url["url"].replace("filename",filename)
        shaList = []
        url["params"]["page"] = page
        html = self.myRequests(url)       
        soup=BeautifulSoup(html.text,'html.parser')
        list_a = soup.findAll("div",class_="commit compare-check-box")        
        if list_a == []:
            return shaList
        for i in range(len(list_a)):
            data = {
            	    "commitSha":list_a[i]["data-id"],
            	    "time":list_a[i]["data-date"].replace(" +0000","")
                	}
            if "删除" not in str(list_a[i]):
                shaList.append(data)
        return shaList 
  
    def add(self,filename,content,key = "",encrypt = True):
        if key == "":
            key = self.key
        filename = self.findFilePath(filename,self.filesys)
        self.unread()
        if encrypt == True:
            content = Gitee.dataManager(content,Gitee.jiaMi,key,self.keyword)
        myList = filename.split("/")
        path = ""
        if len(myList) > 1:
            myList = myList[ :-1]
            for index in range(len(myList)):
                self.createFolder(myList[index],path)
                path +=  myList[index] + "/"
        url = self.giteeUrl["add"].copy()
        url["url"] = url["url"] + path
        url["data"]["authenticity_token"] = self.authenticity_token
        url["data"]["file_name"] = filename.split("/")[-1]
        url["data"]["commit_message_header"] = "add" + filename + "."
        url["data"]["file_path"] = filename
        url["data"]["content"] = content
        
        html = self.myRequests(url)
        
        if html.text.find("新建文件") == -1:
            self.updateFilesys(filename,"add")                       
            return True
        else:
            return False
            
    def createFolder(self,foldername,path = ""):
        self.unread()
        url = self.giteeUrl["createFolder"].copy()
        url["url"] = url["url"] + path
        url["data"]["authenticity_token"] = self.authenticity_token
        url["data"]["new_file_path"] = foldername + "/.keep"
        url["data"]["new_folder_path"] = foldername
         
        html = self.myRequests(url)
        
        if html.text.find(foldername + "/.keep") > -1:
            return True
        else:
            return False
        
    def deleteFolder(self,foldername):
        self.unread()
        url = self.giteeUrl["deleteFolder"].copy()
        url["url"] =  url["url"].replace("foldername",foldername)
        
        html = self.myRequests(url)
        
        if html.text.find("删除成功") > -1:
            self.updateFilesys(foldername,"del")
            return True
        else:
            return False
        
    
    def update(self,filename,content,key = "",encrypt = True):
        if key == "":
            key = self.key
        filename = self.findFilePath(filename,self.filesys)
        if self.getFile(filename) == False:
            self.add(filename,content,key,encrypt)          
        self.unread()
        if encrypt == True:
            content = Gitee.dataManager(content,Gitee.jiaMi,key,self.keyword)
        url = self.giteeUrl["update"].copy()
        url["url"] =  url["url"].replace("filename",filename)
        url["type"] = "get"
        html = self.myRequests(url)
        soup = BeautifulSoup(html.text,"html.parser")
        last_commit_sha = soup.find_all(id="last_commit_sha")[0]['value'] 
        url["data"]["authenticity_token"] = self.authenticity_token
        url["data"]["file_name"] = filename.split("/")[-1]
        url["data"]["commit_message_header"] = "update" + filename + "."
        url["data"]["file_path"] = filename
        url["data"]["content"] = content
        url["data"]["last_commit_sha"] = last_commit_sha
        url["type"] = "post"
        html = self.myRequests(url)
        if html.text.find("更新成功") > -1:
            return True
        else:
            return False
    
        
    def delete(self,filename):
        self.unread()
        filename = self.findFilePath(filename,self.filesys)
        url = self.giteeUrl["delete"].copy()
        url["url"] =  url["url"].replace("filename",filename)
        url["data"]["authenticity_token"] = self.authenticity_token        
        html = self.myRequests(url)
        
        text = self.getFile(filename)
        if text == False:
            self.updateFilesys(filename,"del")
            return True
        else:
            return False
    def findFilePath(self,filename,dict_a):        
        if filename.find("/") > 1:
            return filename
        elif isinstance(dict_a,dict) :
            for x in range(len(dict_a)):
                temp_key = list(dict_a.keys())[x]
                if filename == temp_key:
                    return dict_a[temp_key]
                else:     
                    data = self.findFilePath(filename,dict_a[temp_key])
                    if data != None:
                        return data
    def updateFilesys(self,filename=None,myType=None):
        if filename == None:
            self.filesys = self.getFilesys()      
        else:    
            list_a = filename.split('/')
            data = self.filesys
            if len(list_a) == 1:
                if myType == "add":
                    data[list_a[0]] = filename
                elif myType == "del":
                    del data[list_a[0]]                          
            else:
                for index in range(len(list_a)):
                    data = data[list_a[index]]
                    if (index + 2) == len(list_a):
                        if myType == "add":
                            data[list_a[index+1]] = filename
                        elif myType == "del":
                            del data[list_a[index+1]]
                        break
        self.gitee["filesys"] = self.filesys   
        self.update("gitee.json",json.dumps(self.gitee,indent=4,ensure_ascii=False))           
    
    #gitee文件树状列表
    def getFilesys(self):
        url = "https://gitee.com/" + self.cangku + "/tree/master"
        myDict={}
        self.traverse(url,myDict)
        return myDict
    #遍历文件文件夹
    def traverse(self,url,myDict):
        html = self.session.get(url).text
        soup = BeautifulSoup(html, 'html.parser')
        # 提取页面的头部信息， 解决乱码问题
        headObj = soup.head
        folderObj = soup.find_all('div', class_=re.compile("five wide column tree-item-file-name"))
        for index in range(len(folderObj)):
            name = folderObj[index].a.get("title")
            path = folderObj[index].get("data-path")
            mytype = folderObj[index].get("data-type")
            if mytype == "file":
                myDict[name] = path                
            elif mytype == "folder":
                myDict[name] = {}
                url0= url + "/" + name
                self.traverse(url0,myDict[name])
    def unread(self):
        self.session.headers["X-CSRF-Token"] = self.authenticity_token
        url = self.giteeUrl["unread"]
        html = self.myRequests(url)
        if html.status_code == 200:
            return True
        else:
            return False           